#include "messages_channels.hpp"

#include "messages_container.hpp"
#include "channels_container.hpp"

#include <iostream>
namespace fasys{


messages_channels::~messages_channels()
{
  delete _messages;
  delete _channels;
  delete _global_channels;
}

messages_channels::messages_channels()
  : _messages(new messages_container)
  , _channels(new channels_container)
  , _global_channels(0)
{
}

messages_channels::messages_channels(channels_container* global_channels)
  : _messages(new messages_container)
  , _channels(new channels_container)
  , _global_channels(global_channels)
{
}

messages_channels::messages_channels( const messages_channels& mc)
{
}

channel_info messages_channels::_create_channel( const std::string& channel )
{
  if ( _global_channels )
  {
    if ( channel_info ci = _global_channels->create( channel ) )
      return _channels->create( channel, ci.channel_id );
    return channel_info();
  }
  return _channels->create( channel );
}

channel_id_t messages_channels::create(const std::string& channel, size_t min_size, size_t max_size)
{
  channel_info ci = this->_create_channel(channel);
  if ( ci )
  {
    ci.min_size = min_size;
    ci.max_size = max_size;
    _channels->update(ci);
  }

  return ci.channel_id;
}

channel_id_t messages_channels::create(const std::string& channel)
{
  return this->_create_channel(channel).channel_id;
}

void messages_channels::publish(message& m)
{
  if ( channel_info ci = this->_create_channel(m.channel) )
  {
    m.channel_id = ci.channel_id;
    if ( m.lifetime == 0 )
      m.lifetime = FASYS_DEFAULT_TIMELIFE;

    if ( m.limit < ci.min_size )
      m.limit = ci.min_size;

    if ( m.limit > ci.max_size )
      m.limit = ci.max_size;

    m.message_id = ++ci.message_id_counter;
    message_info mi(ci.channel_id, m.message_id, m.lifetime, m.order, m.limit );

    m.death_timespan = m.lifetime;
    if ( !m.content.empty() )
      mi.attach( new data_t(m.content) );

    m.count = _messages->insert(mi);

    _channels->update(ci);
  }
}

/// вставка ранее опубликованного сообщения
channel_id_t messages_channels::republish(const message& m)
{
  if ( channel_info ci = this->_create_channel(m.channel) )
  {
    message_info mi(ci.channel_id, m.message_id, m.lifetime, m.order, m.limit );
    if ( !m.content.empty() )
      mi.attach( new data_t(m.content) );
    _messages->insert(mi);
    _channels->update(ci);
    return mi.channel_id;
  }
  return 0;
}

struct f_detach
{
  channels_container* cc;
  std::list<message>& ml;
  std::string channel;
  f_detach(channels_container* cc, std::list<message>& ml)
    : cc(cc), ml(ml)
  {}

  void operator()(const message_info& mi)
  {
    if ( channel.empty() )
    {
      if ( channel_info ci = cc->find(mi.channel_id) )
        channel = ci.name;
    }

    ml.push_back( message() );
    message &m = ml.back();
    m.message_id = mi.message_id;
    m.channel_id = mi.channel_id;
    m.order = mi.order;
    m.create_time = mi.create_time;
    m.lifetime = mi.lifetime;
    m.death_time = mi.death_time;
    m.limit = mi.limit;
    mi.data->swap(m.content);

    /** контент сообщения. */
    // mutable data_t* data;

    m.channel = channel;
  }
};

void messages_channels::move_all( std::list<message>& ml)
{
  while ( _messages->size() > 0 )
  {
    _messages->detach_first_by_order(f_detach(_channels, ml), 0, size_t(-1) );
  }
}

}

